package ssdb

import (
	"fmt"
	"github.com/seefan/gossdb/v2"
	"github.com/seefan/gossdb/v2/conf"
	"github.com/seefan/gossdb/v2/pool"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	mapClient = sync.Map{}      // 连接
	lock sync.Mutex             // 连接锁定防止并发访问

	contentMap = sync.Map{}     //数据库
	contentMu sync.Mutex        //数据库锁定防止并发访问

	repositoryMap = sync.Map{}  //仓储
	repositoryMu = sync.Mutex{} //仓储锁定防止并发访问
)

//GoSSDBContent 集合上下文
type GoSSDBContent struct {
	pool    *pool.Connectors
	ipPort  string
	timeout time.Duration
}

//GetGoSSDBRepository 获取集合仓储
// entity 结构体
func (s *GoSSDBContent) GetGoSSDBRepository(prefixName string) ISSdbRepository {
	key := fmt.Sprint(s.ipPort, "@#@", prefixName)
	value, ok := repositoryMap.Load(key)
	if ok {
		return value.(ISSdbRepository)
	}
	repositoryMu.Lock()
	defer repositoryMu.Unlock()

	value, ok = repositoryMap.Load(key)
	if ok {
		return value.(ISSdbRepository)
	}
	repository := &SSdbRepository{
		pool:       s.pool,
		entityName: prefixName,
		timeout:    s.timeout,
	}
	repositoryMap.Store(key, repository)
	return repository
}

//DataBaseMapping 添加映射
// @client 连接对象
// @dataName 数据库名
func DataBaseMapping(config map[string]string) (*GoSSDBContent,error) {
	ipPort := config["ipport"]
	value, ok := contentMap.Load(ipPort)
	if ok {
		return value.(*GoSSDBContent), nil
	}

	contentMu.Lock()
	defer contentMu.Unlock()

	value, ok = contentMap.Load(ipPort)
	if ok {
		return value.(*GoSSDBContent), nil
	}

	timeout := time.Second * 30
	if timeoutStr, ok := config["timeout"]; ok {
		atoi, err := strconv.Atoi(timeoutStr)
		if err == nil {
			timeout = time.Second * time.Duration(atoi)
		}
	}

	pool, err := GetSSdbPool(config)
	if err != nil {
		return nil, err
	}
	content := &GoSSDBContent{
		pool:    pool,
		ipPort:  ipPort,
		timeout: timeout,
	}
	contentMap.Store(ipPort, content)
	return content, nil
}

//GetSSdbPool 获取ssdb连接池
func GetSSdbPool(config map[string]string) (*pool.Connectors, error) {
	keyConn := config["ipport"]
	val, ok := mapClient.Load(keyConn)
	if ok {
		return val.(*pool.Connectors), nil
	}
	lock.Lock()
	defer lock.Unlock()

	/*二次获取防止并发多次连接*/
	val, ok = mapClient.Load(keyConn)
	if ok {
		return val.(*pool.Connectors), nil
	}

	newPool, err := connect(config)
	if err != nil {
		return nil, err
	}
	mapClient.Store(keyConn, newPool)
	return newPool, nil
}

//connect 获取客户端
func connect(config map[string]string) (*pool.Connectors, error) {
	keyConnAry := strings.Split(config["ipport"], ":")
	port, err := strconv.Atoi(keyConnAry[1])
	if err != nil {
		return nil, err
	}
	password, ok := config["password"]
	if !ok {
		password = ""
	}
	minPoolSize := 10
	minPoolSizeStr, ok := config["MinPoolSize"]
	if ok {
		atoi, err := strconv.Atoi(minPoolSizeStr)
		if err == nil {
			minPoolSize = atoi
		}
	}
	maxPoolSize := 200
	maxPoolSizeStr, ok := config["MaxPoolSize"]
	if ok {
		atoi, err := strconv.Atoi(maxPoolSizeStr)
		if err == nil {
			maxPoolSize = atoi
		}
	}
	pool, err := gossdb.NewPool(&conf.Config{
		Host:         keyConnAry[0],
		Port:         port,
		MinPoolSize:  minPoolSize,
		MaxPoolSize:  maxPoolSize,
		Password:     password,
		RetryEnabled: true,
		AutoClose:    true,
	})
	if err != nil {
		return nil, err
	}
	return pool, nil
}